package work.jame.blog.framework.limit.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import work.jame.blog.framework.limit.AbstractRequestLimitHandler;
import work.jame.blog.framework.result.Result;
import work.jame.blog.framework.utils.http.IpUtils;
import work.jame.blog.framework.utils.http.WebUtils;
import work.jame.blog.framework.utils.redis.RedisService;
import work.jame.blog.framework.utils.redis.RedisUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

/**
 * 当同一ip发送请求非常多后将此ip禁用一段时间
 * 默认一小时内5千次
 *
 * @author : Jame
 * @date : 2023-05-13 11:25
 **/
@Component
public class TooManyRequestLimitHandler extends AbstractRequestLimitHandler {

    @Value("${request-limit.max-request-number}")
    private long maxRequestNumberInHour = 5000;

    @Autowired
    private RedisService redisService;

    /**
     * 如果一个ip在1小时内总计发送超过最大请求次数,则禁用1天
     * 默认请求次数为5000千
     */
    @Override
    public Operation handler(HttpServletRequest request, HttpServletResponse response) {
        String ip = IpUtils.getIpAddr(request);
        //ip请求计数key
        String ipKey = RedisUtils.getTooManyRequestLimitIpKey(ip);
        //已经被禁用的ip key
        String disableRequestIpKey = RedisUtils.getDisableRequestIpKey(ip);
        //如果这个请求已经被禁用,则直接返回ture 禁止此次请求
        if (redisService.hasKey(disableRequestIpKey)) {
            Result<?> result = Result.create(null, HttpStatus.TOO_MANY_REQUESTS.value(), "你的请求太多啦!,已被禁用一天");
            WebUtils.respondResult(response, result);
            return Operation.PROHIBIT;
        }
        if (redisService.hasKey(ipKey)) {
            Integer requestNumber = redisService.getCacheObject(ipKey);
            //超出了最大限制则禁用一天
            if (requestNumber > maxRequestNumberInHour) {
                Result<?> result = Result.create(null, HttpStatus.TOO_MANY_REQUESTS.value(), "你的请求太多啦!,已被禁用一天");
                WebUtils.respondResult(response, result);
                redisService.setCacheObject(disableRequestIpKey, null, 1L, TimeUnit.DAYS);
                return Operation.PROHIBIT;
            }
            //没有超出限制-将请求次数加1
            long expire = redisService.getExpire(ipKey);
            redisService.setCacheObject(ipKey, ++requestNumber, expire, TimeUnit.SECONDS);
        } else {
            redisService.setCacheObject(ipKey, 1, 1L, TimeUnit.HOURS);
        }

        return Operation.ALLOW;
    }
}
